//! Zig implementation of Asterinas system call handling
//! SPDX-License-Identifier: MPL-2.0

const std = @import("std");
const builtin = @import("builtin");

// Core system call infrastructure
pub const SyscallError = @import("error.zig").SyscallError;
pub const SyscallReturn = @import("return.zig").SyscallReturn;
pub const Context = @import("context.zig").Context;
pub const UserSpace = @import("user_space.zig").UserSpace;

// Architecture-specific dispatching
pub const arch = switch (builtin.cpu.arch) {
    .x86_64 => @import("arch/x86_64.zig"),
    .riscv64 => @import("arch/riscv64.zig"),
    .aarch64 => @import("arch/aarch64.zig"),
    else => @compileError("Unsupported architecture"),
};

// System call implementations
pub const fs = @import("fs/mod.zig");
pub const process = @import("process/mod.zig");
pub const memory = @import("memory/mod.zig");
pub const network = @import("network/mod.zig");
pub const time = @import("time/mod.zig");
pub const signal = @import("signal/mod.zig");
pub const ipc = @import("ipc/mod.zig");

// Constants and types
pub const constants = @import("constants.zig");
pub const types = @import("types.zig");

// Core system call numbers (Linux ABI compatible)
pub const SyscallNumber = enum(u64) {
    // File system operations
    read = 0,
    write = 1,
    open = 2,
    close = 3,
    stat = 4,
    fstat = 5,
    lstat = 6,
    poll = 7,
    lseek = 8,
    mmap = 9,
    mprotect = 10,
    munmap = 11,
    brk = 12,
    
    // Process management
    fork = 57,
    vfork = 58,
    execve = 59,
    exit = 60,
    wait4 = 61,
    kill = 62,
    clone = 56,
    
    // Memory management
    madvise = 28,
    mremap = 25,
    msync = 26,
    
    // Network operations
    socket = 41,
    connect = 42,
    accept = 43,
    sendto = 44,
    recvfrom = 45,
    bind = 49,
    listen = 50,
    
    // Time operations
    gettimeofday = 96,
    time = 201,
    nanosleep = 35,
    
    // Signal handling
    rt_sigaction = 13,
    rt_sigprocmask = 14,
    rt_sigreturn = 15,
    
    // IPC
    pipe = 22,
    eventfd = 284,
    
    // Special
    restart_syscall = 219,
    exit_group = 231,
    
    _,
};

// Main system call dispatcher
pub const SyscallDispatcher = struct {
    const Self = @This();
    
    /// Dispatch a system call based on number and arguments
    pub fn dispatch(
        syscall_num: u64,
        arg0: u64,
        arg1: u64,
        arg2: u64,
        arg3: u64,
        arg4: u64,
        arg5: u64,
        context: *Context,
    ) SyscallReturn {
        const num: SyscallNumber = @enumFromInt(syscall_num);
        
        return switch (num) {
            // File system operations
            .read => fs.sys_read(@intCast(arg0), arg1, @intCast(arg2), context),
            .write => fs.sys_write(@intCast(arg0), arg1, @intCast(arg2), context),
            .open => fs.sys_open(arg0, @intCast(arg1), @intCast(arg2), context),
            .close => fs.sys_close(@intCast(arg0), context),
            .stat => fs.sys_stat(arg0, arg1, context),
            .fstat => fs.sys_fstat(@intCast(arg0), arg1, context),
            .lstat => fs.sys_lstat(arg0, arg1, context),
            .poll => fs.sys_poll(arg0, @intCast(arg1), @intCast(arg2), context),
            .lseek => fs.sys_lseek(@intCast(arg0), @intCast(arg1), @intCast(arg2), context),
            
            // Memory management
            .mmap => memory.sys_mmap(arg0, @intCast(arg1), @intCast(arg2), @intCast(arg3), @intCast(arg4), @intCast(arg5), context),
            .mprotect => memory.sys_mprotect(arg0, @intCast(arg1), @intCast(arg2), context),
            .munmap => memory.sys_munmap(arg0, @intCast(arg1), context),
            .brk => memory.sys_brk(arg0, context),
            .madvise => memory.sys_madvise(arg0, @intCast(arg1), @intCast(arg2), context),
            .mremap => memory.sys_mremap(arg0, @intCast(arg1), @intCast(arg2), @intCast(arg3), arg4, context),
            .msync => memory.sys_msync(arg0, @intCast(arg1), @intCast(arg2), context),
            
            // Process management
            .fork => process.sys_fork(context),
            .vfork => process.sys_vfork(context),
            .execve => process.sys_execve(arg0, arg1, arg2, context),
            .exit => process.sys_exit(@intCast(arg0), context),
            .wait4 => process.sys_wait4(@intCast(arg0), arg1, @intCast(arg2), arg3, context),
            .kill => process.sys_kill(@intCast(arg0), @intCast(arg1), context),
            .clone => process.sys_clone(@intCast(arg0), arg1, arg2, arg3, arg4, context),
            .exit_group => process.sys_exit_group(@intCast(arg0), context),
            
            // Network operations
            .socket => network.sys_socket(@intCast(arg0), @intCast(arg1), @intCast(arg2), context),
            .connect => network.sys_connect(@intCast(arg0), arg1, @intCast(arg2), context),
            .accept => network.sys_accept(@intCast(arg0), arg1, arg2, context),
            .sendto => network.sys_sendto(@intCast(arg0), arg1, @intCast(arg2), @intCast(arg3), arg4, @intCast(arg5), context),
            .recvfrom => network.sys_recvfrom(@intCast(arg0), arg1, @intCast(arg2), @intCast(arg3), arg4, arg5, context),
            .bind => network.sys_bind(@intCast(arg0), arg1, @intCast(arg2), context),
            .listen => network.sys_listen(@intCast(arg0), @intCast(arg1), context),
            
            // Time operations
            .gettimeofday => time.sys_gettimeofday(arg0, arg1, context),
            .time => time.sys_time(arg0, context),
            .nanosleep => time.sys_nanosleep(arg0, arg1, context),
            
            // Signal handling
            .rt_sigaction => signal.sys_rt_sigaction(@intCast(arg0), arg1, arg2, @intCast(arg3), context),
            .rt_sigprocmask => signal.sys_rt_sigprocmask(@intCast(arg0), arg1, arg2, @intCast(arg3), context),
            .rt_sigreturn => signal.sys_rt_sigreturn(context),
            
            // IPC
            .pipe => ipc.sys_pipe(arg0, context),
            .eventfd => ipc.sys_eventfd(@intCast(arg0), @intCast(arg1), context),
            
            // Special handling
            .restart_syscall => SyscallReturn.restart(),
            
            // Unknown system call
            _ => SyscallReturn.error_return(SyscallError.ENOSYS),
        };
    }
    
    /// Get system call name for debugging
    pub fn syscall_name(num: u64) []const u8 {
        const syscall_num: SyscallNumber = @enumFromInt(num);
        return @tagName(syscall_num);
    }
    
    /// Validate system call arguments
    pub fn validate_args(
        syscall_num: u64,
        args: [6]u64,
        context: *Context,
    ) SyscallError!void {
        _ = syscall_num;
        _ = args;
        _ = context;
        // Basic validation - can be extended per syscall
        // For now, just return success
    }
};

// Performance monitoring
pub const SyscallStats = struct {
    call_count: std.atomic.Value(u64),
    total_time_ns: std.atomic.Value(u64),
    error_count: std.atomic.Value(u64),
    
    const Self = @This();
    
    pub fn init() Self {
        return Self{
            .call_count = std.atomic.Value(u64).init(0),
            .total_time_ns = std.atomic.Value(u64).init(0),
            .error_count = std.atomic.Value(u64).init(0),
        };
    }
    
    pub fn record_call(&self, duration_ns: u64, is_error: bool) void {
        _ = self.call_count.fetchAdd(1, .monotonic);
        _ = self.total_time_ns.fetchAdd(duration_ns, .monotonic);
        if (is_error) {
            _ = self.error_count.fetchAdd(1, .monotonic);
        }
    }
    
    pub fn get_stats(self: *const Self) struct { calls: u64, total_time: u64, errors: u64 } {
        return .{
            .calls = self.call_count.load(.monotonic),
            .total_time = self.total_time_ns.load(.monotonic),
            .errors = self.error_count.load(.monotonic),
        };
    }
};

// Global syscall statistics
var global_stats: SyscallStats = SyscallStats.init();

/// Main system call entry point with performance monitoring
pub fn handle_syscall(
    syscall_num: u64,
    arg0: u64,
    arg1: u64,
    arg2: u64,
    arg3: u64,
    arg4: u64,
    arg5: u64,
    context: *Context,
) callconv(.C) u64 {
    const start_time = std.time.nanoTimestamp();
    
    // Validate arguments
    const args = [_]u64{ arg0, arg1, arg2, arg3, arg4, arg5 };
    SyscallDispatcher.validate_args(syscall_num, args, context) catch |err| {
        const duration = @as(u64, @intCast(std.time.nanoTimestamp() - start_time));
        global_stats.record_call(duration, true);
        return @intFromEnum(err);
    };
    
    // Dispatch the system call
    const result = SyscallDispatcher.dispatch(
        syscall_num,
        arg0, arg1, arg2, arg3, arg4, arg5,
        context,
    );
    
    // Record performance metrics
    const duration = @as(u64, @intCast(std.time.nanoTimestamp() - start_time));
    const is_error = result.is_error();
    global_stats.record_call(duration, is_error);
    
    return result.to_raw();
}

// Export C-compatible functions for Rust integration
export fn zig_handle_syscall(
    syscall_num: u64,
    arg0: u64,
    arg1: u64,
    arg2: u64,
    arg3: u64,
    arg4: u64,
    arg5: u64,
    context: *Context,
) u64 {
    return handle_syscall(syscall_num, arg0, arg1, arg2, arg3, arg4, arg5, context);
}

export fn zig_get_syscall_name(num: u64) [*:0]const u8 {
    const name = SyscallDispatcher.syscall_name(num);
    // Note: This assumes the name is null-terminated, which it should be for enum tags
    return @ptrCast(name.ptr);
}

export fn zig_get_syscall_stats() extern struct {
    calls: u64,
    total_time: u64,
    errors: u64,
} {
    const stats = global_stats.get_stats();
    return .{
        .calls = stats.calls,
        .total_time = stats.total_time,
        .errors = stats.errors,
    };
}

// Testing support
test "syscall dispatcher" {
    const testing = std.testing;
    
    // Test syscall name resolution
    const name = SyscallDispatcher.syscall_name(0);
    try testing.expectEqualStrings("read", name);
    
    // Test stats initialization
    var stats = SyscallStats.init();
    const initial = stats.get_stats();
    try testing.expectEqual(@as(u64, 0), initial.calls);
    try testing.expectEqual(@as(u64, 0), initial.total_time);
    try testing.expectEqual(@as(u64, 0), initial.errors);
    
    // Test stats recording
    stats.record_call(1000, false);
    const after_call = stats.get_stats();
    try testing.expectEqual(@as(u64, 1), after_call.calls);
    try testing.expectEqual(@as(u64, 1000), after_call.total_time);
    try testing.expectEqual(@as(u64, 0), after_call.errors);
}